package logger

import (
	"context"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
	"time"
)

type GormZapLoggerAdapter struct {
	Logger                              *zap.Logger
	infoStr, warnStr, errStr            string
	traceStr, traceErrStr, traceWarnStr string
	logger.Config
}

func NewGormZapLoggerAdapter(l *zap.Logger) logger.Interface {
	var (
		infoStr      = "%s\n[info] "
		warnStr      = "%s\n[warn] "
		errStr       = "%s\n[error] "
		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
	)

	config := logger.Config{
		SlowThreshold:             time.Second, // 慢 SQL 阈值
		LogLevel:                  logger.Info, // 日志级别
		IgnoreRecordNotFoundError: true,        // 是否忽略ErrRecordNotFound（记录未找到）错误
		Colorful:                  false,       // 是否彩色打印
	}

	if config.Colorful {
		infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[info] " + logger.Reset
		warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[warn] " + logger.Reset
		errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[error] " + logger.Reset
		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
		traceWarnStr = logger.Green + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
	}

	newLogger := &GormZapLoggerAdapter{
		Logger:       l,
		infoStr:      infoStr,
		warnStr:      warnStr,
		errStr:       errStr,
		traceStr:     traceStr,
		traceWarnStr: traceWarnStr,
		traceErrStr:  traceErrStr,
		Config:       config,
	}

	return newLogger
}

func (gl *GormZapLoggerAdapter) LogMode(level logger.LogLevel) logger.Interface {
	newlogger := *gl
	newlogger.LogLevel = level
	return &newlogger
}

func (gl *GormZapLoggerAdapter) Info(ctx context.Context, msg string, data ...interface{}) {
	if gl.LogLevel >= logger.Info {
		gl.Logger.Sugar().Infof(gl.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

func (gl *GormZapLoggerAdapter) Warn(ctx context.Context, msg string, data ...interface{}) {
	if gl.LogLevel >= logger.Warn {
		gl.Logger.Sugar().Warnf(gl.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

func (gl *GormZapLoggerAdapter) Error(ctx context.Context, msg string, data ...interface{}) {
	if gl.LogLevel >= logger.Error {
		gl.Logger.Sugar().Errorf(gl.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

func (gl *GormZapLoggerAdapter) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	if gl.LogLevel <= logger.Silent {
		return
	}

	elapsed := time.Since(begin)
	switch {
	case err != nil && gl.LogLevel >= logger.Error && (!errors.Is(err, logger.ErrRecordNotFound) || !gl.IgnoreRecordNotFoundError):
		sql, rows := fc()
		if rows == -1 {
			gl.Logger.Sugar().Errorf(gl.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			gl.Logger.Sugar().Errorf(gl.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	case elapsed > gl.SlowThreshold && gl.SlowThreshold != 0 && gl.LogLevel >= logger.Warn:
		sql, rows := fc()
		slowLog := fmt.Sprintf("SLOW SQL >= %v", gl.SlowThreshold)
		if rows == -1 {
			gl.Logger.Sugar().Warnf(gl.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			gl.Logger.Sugar().Warnf(gl.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, sql)
		}
	case gl.LogLevel == logger.Info:
		sql, rows := fc()
		if rows == -1 {
			gl.Logger.Sugar().Infof(gl.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			gl.Logger.Sugar().Infof(gl.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, sql)
		}
	}
}
